package progweb.project.Manager;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.ResultSet;
import com.mysql.jdbc.Statement;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import org.apache.log4j.*;



/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Davide Bonomelli, Valentino Briani
 * @version 1.0.0
 * Classe per la gestione del data base. Ogni operazione sul DB verrà eseguita
 * da questa classe.
 */
public class DBManager {
    private static org.apache.log4j.Logger log=Logger.getLogger(DBManager.class);
    private static String host="localhost";
    private static String dbName="Vaccinazioni";
    private static int port=3306;
    private static String user="root";
    private static String password="";
    private static Connection myConnection;
    
    /**
     * Funzione che permette di stabilire una connessione con il DB MySQL
     * @return Ritorna una connessione 
     */
    public static com.mysql.jdbc.Connection getConnection(){
        Connection c=null;
        try{
            String dbString="jdbc:mysql://"+DBManager.host+"/"+DBManager.dbName;
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            c = (Connection) DriverManager.getConnection(dbString, DBManager.user, DBManager.password);
        }catch(Exception e){
            log.error("", e);
        }
        return c;
    }
    
    /**
     * Metodo che inserisce nuovi Richiami nel DB
     * @param data: data del richiamo
     * @param pazienti: pazienti che sono stati vaccinati
     * @param m: medico che ha effettuato i richiami
     */
    public static void addRichiamiToDB(Timestamp data, ArrayList<Paziente> pazienti, Medico m) {
        //Apro la connessione
        DBManager.myConnection=DBManager.getConnection();
        //Scorro l'ArrayList di Pazienti
        ListIterator<Paziente> i=pazienti.listIterator();
        Paziente p;
        while (i.hasNext()) {
            p=i.next();
            //Stringa della query
            String query="INSERT INTO Richiami VALUES (NULL, "+p.getId()+", "+m.getId()+", \""+data.toString().substring(0, 19)+"\")";
            executeStatement(query, myConnection);
        }
    }
    
    /**
     * Metodo per ottenere l'istanza della classe medico fornendo il suo id
     * @param idMedico id del medico di cui si cercano i dati
     * @return Ritorna un' istanza della classe Medico
     */
    public static Medico getMedicoFromId(int idMedico){
        Medico medico=null;
        //Apro la connessione
        DBManager.myConnection=DBManager.getConnection();
        //Stringa della query
        String query= "SELECT * FROM `Medici` WHERE `id_medico` = "+idMedico;
        try{
            //Creo uno Statement
            Statement stmt= (Statement) DBManager.myConnection.createStatement();
            //Eseguo la query
            ResultSet result = (ResultSet) stmt.executeQuery(query);
            //Uso il risultato della query e chiudo la connessione
            while(result.next()){
                medico = new Medico();
                medico.setId(Integer.parseInt(result.getString("id_medico")));
                medico.setNome(result.getString("nome"));
                medico.setCognome(result.getString("cognome"));
                medico.setPassword(result.getString("password"));
            }
            stmt.close();
            result.close();
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return medico;
    }
    
    /**
     * Metodo che permette di ottenere l'istanza della classe Paziente, partendo
     * dal suo id, contenente tutti i dati. 
     * @param idPaziente id del Paziente cercato
     * @return istanza della classe Paziente contenente i dati
     */
    public static Paziente getPazienteFromId(int idPaziente){
        Paziente paziente=null;
        DBManager.myConnection=DBManager.getConnection();
        String query="SELECT * FROM `Pazienti` WHERE `id_paziente` = "+idPaziente;
        try{
            //Eseguo la query e chiudo la connessione
            Statement stmt=(Statement) DBManager.myConnection.createStatement();
            ResultSet result = (ResultSet) stmt.executeQuery(query); 
            while(result.next()){
                paziente = new Paziente();
                paziente.setId(Integer.parseInt(result.getString("id_paziente")));
                paziente.setNome(result.getString("nome"));
                paziente.setCognome(result.getString("cognome"));
                paziente.setAnnoDiNascita(result.getDate("data_di_nascita"));
                paziente.setURLFoto(result.getString("foto"));
                paziente.setPassword(result.getString("password"));
            }
            stmt.close();
            result.close();
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return paziente;
    }
    
    /**
     * Metodo che permette di ottenere un'ArrayList di oggetti della classe Paziente, contentente
     * tutti i pazienti per i quali dall'ultimo richiamo è passato più tempo di quello passato 
     * come parametro (in secondi) alla funzione
     * @param secondi: secondi dall'ultimo richiamo
     * @return ArrayList di Pazienti
     */
    public static ArrayList<Paziente> getPazientiFromSecondiDaUltimoRichiamo(long secondi){
        ArrayList<Paziente> pazienti=new ArrayList<Paziente>();
        DBManager.myConnection=DBManager.getConnection();
        String query="SELECT * FROM `Pazienti` WHERE `id_paziente` NOT IN (SELECT DISTINCT `paziente` FROM `Richiami` WHERE `data` >= TIMESTAMP '"+new Timestamp(System.currentTimeMillis()-(secondi*1000)) +"')";
        try{
            //Eseguo la query e chiudo la connessione
            Statement stmt=(Statement)DBManager.myConnection.createStatement();
            ResultSet result=(ResultSet)stmt.executeQuery(query);
            Paziente paziente=null;
            while(result.next()){
                paziente=new Paziente();
                paziente.setId(Integer.parseInt(result.getString("id_paziente")));
                paziente.setNome(result.getString("nome"));
                paziente.setCognome(result.getString("cognome"));
                paziente.setAnnoDiNascita(result.getDate("data_di_nascita"));
                paziente.setURLFoto(result.getString("foto"));
                paziente.setPassword(result.getString("password"));
                paziente.setUltimoRichiamo(getUltimoRichiamoFromIdPaziente(paziente.getId()));
                pazienti.add(paziente);
            }
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return pazienti;
    }
    
    /**
     * Metodo che permette di ottenere un ArrayList di oggetti della classe Richiamo aventi data succcessiva
     * a quella passata come parametro
     * @param data: data minima con cui selezionare i richiami
     * @return ArrayList di oggetti della classe Richiamo
     */
    public static ArrayList<Richiamo> getRichiamiFromData(Timestamp data) {
        ArrayList<Richiamo> richiami=new ArrayList<Richiamo>();
        DBManager.myConnection=DBManager.getConnection();
        String query="SELECT * FROM `Richiami` WHERE `data` > TIMESTAMP '"+data+"' ORDER BY `richiami`.`data` ASC";
        try{
            //Eseguo la query e chiudo la connessione
            Statement stmt=(Statement) DBManager.myConnection.createStatement();
            ResultSet result=(ResultSet)stmt.executeQuery(query);
            Richiamo richiamo=null;
            while(result.next()){
                richiamo=new Richiamo();
                richiamo.setId(Integer.parseInt(result.getString("id_richiamo")));
                richiamo.setMedico(DBManager.getMedicoFromId(result.getInt("medico")));
                richiamo.setPaziente(DBManager.getPazienteFromId(result.getInt("paziente")));
                richiamo.setData(Timestamp.valueOf(result.getString("data")));
                richiami.add(richiamo);
            }
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return richiami;
    }
    
    /**
     * Metodo che permette di ottenere l'istanza della classe Richiamo, partendo
     * dal suo id, contenente tutti i dati. 
     * @param idRichiamo id del richiamo cercato
     * @return istanza della classe Richiamo contenente i dati
     */
    public static Richiamo getRichiamoFromId(int idRichiamo){
        Richiamo richiamo=null;
        DBManager.myConnection=DBManager.getConnection();
        String query="SELECT * FROM `Richiami` WHERE `id_richiamo` = "+idRichiamo;
        try{
            //Eseguo la query e chiudo la connessione
            Statement stmt=(Statement) DBManager.myConnection.createStatement();
            ResultSet result = (ResultSet) stmt.executeQuery(query); 
            while(result.next()){
                richiamo = new Richiamo();
                richiamo.setId(Integer.parseInt(result.getString("id_richiamo")));
                richiamo.setMedico(DBManager.getMedicoFromId(result.getInt("medico")));
                richiamo.setPaziente(DBManager.getPazienteFromId(result.getInt("paziente")));
                richiamo.setData(Timestamp.valueOf(result.getString("data")));
            }
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return richiamo;
    }
    
    /**
     * Serve per ottenere da un paziente conosciuto il numero di richiami che
     * ha fatto fino ad ora.
     * 
     * @param p l'istanza paziente contenente i dati del paziente per 
     * cui si cercano i richiami effettuati
     * @return Un ArrayList contenente tanti oggetti richiamo quante sono le
     * corrispondenze trovate nel DB
     */
    public static ArrayList getRichiamiFromIdPaziente(Paziente p){
        ArrayList richiami = null;
        Richiamo richiamo = null;
        DBManager.myConnection=DBManager.getConnection();
        String query="SELECT * FROM `Richiami` WHERE `paziente` = "+p.getId()+" ORDER BY `richiami`.`data` ASC";
        try{
            //Eseguo la query e chiudo la connessione
            Statement stmt=(Statement) DBManager.myConnection.createStatement();
            ResultSet result = (ResultSet) stmt.executeQuery(query); 
            richiami = new ArrayList();
            while(result.next()){
                richiamo = new Richiamo();
                richiamo.setId(Integer.parseInt(result.getString("id_richiamo")));
                richiamo.setMedico(DBManager.getMedicoFromId(result.getInt("medico")));
                richiamo.setPaziente(DBManager.getPazienteFromId(result.getInt("paziente")));
                richiamo.setData(Timestamp.valueOf(result.getString("data")));
                richiami.add(richiamo);
            }
            stmt.close();
            result.close();
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return richiami;
    }
    
    /**
     * Metodo che permette di ottenere la data, in formato String, dell'ultimo richiamo
     * di un Paziente, partendo dall'id del Paziente. 
     * @param idRichiamo: id del Paziente del quale si vuole la data dell'ultimo richiamo
     * @return la data, in formato String, dell'ultimo richiamo del Paziente
     */
    public static String getUltimoRichiamoFromIdPaziente(int idPaziente){
        DBManager.myConnection=DBManager.getConnection();
        String query="SELECT MAX(`data`) AS `ultimo_richiamo` FROM `Richiami` WHERE `paziente` = "+idPaziente;
        String data="";
        try{
            //Eseguo la query e chiudo la connessione
            Statement stmt=(Statement)DBManager.myConnection.createStatement();
            ResultSet result=(ResultSet)stmt.executeQuery(query); 
            while(result.next()){
                data=result.getString("ultimo_richiamo");
                if (data==null)
                    data="";
            }
            stmt.close();
            result.close();
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return data;
    }
    
    /**
     * Funzione che controlla, dati unser name e password, se l'utente esiste 
     * ed il tipo d'utente.
     * @param u_name username da controllare
     * @param password password da controllare
     * @return Ritorna -1 se i dati non esistono nel DB, 1 se esistono e sono di 
     * un dottore e 2 se esistono ma sono di un paziente. 
     */
    public static int checkUserNameAndPwd(String u_name,String password){
        int typeOfUser = -1;
        DBManager.myConnection=DBManager.getConnection();
        String qDottoriTbl = "SELECT * FROM `Medici` WHERE `nome` = "+u_name+" AND `password` = "+password;
        String qPazientiTbl = "SELECT * FROM `Pazienti` WHERE `nome` = "+u_name+" AND `password` = "+password;
        try{
            Statement stmt = (Statement) DBManager.myConnection.createStatement();
            ResultSet rDottori = (ResultSet) stmt.executeQuery(qDottoriTbl);
            ResultSet rPazienti = (ResultSet) stmt.executeQuery(qPazientiTbl);
            
            if(rDottori.next())
                typeOfUser= 1;
            else if(rPazienti.next())
                typeOfUser= 2;
            else
                typeOfUser= -1;
            rDottori.close();
            rPazienti.close();
            stmt.close();
            DBManager.myConnection.close();
        }catch(Exception e){
            log.error("", e);
        }
        return -1;
    }

    /**
     * Metodo per eseguire azioni sul db
     * @param q comando da eseguire sul db
     * @param con connessione al db
     */        
    public static void executeStatement(String q, Connection con){
        try{
            Statement stmt=(Statement) con.createStatement();
            stmt.execute(q);
            stmt.close();
        } catch(Exception e){
            log.error("", e);
        }    
    }
    
    /**
     * restituisce i risultati della query
     @param query la query da eseguire
     * @param con connessione al db
     * @return il risultato della query
     * @exception restituisce null in caso di eccezione
     */
    public static ResultSet executeQuery(String query, Connection con){
        try{
            Statement stmt=(Statement)con.createStatement();
            ResultSet results=(ResultSet) stmt.executeQuery(query);
            return results;
        }catch(Exception e){            
            log.error("", e);
            return null;
        } 
    }
    
    public static void main(String args[]){
        Medico m=DBManager.getMedicoFromId(1);
        Paziente p = DBManager.getPazienteFromId(45);
        Richiamo r = DBManager.getRichiamoFromId(2);
        log.debug("Medico: "+m);
        log.debug("Paziente: "+p);
        log.debug("Richiamo: "+r);
        ArrayList l = DBManager.getRichiamiFromIdPaziente(p);
        Iterator i = l.iterator();
        
        while(i.hasNext()){
            log.debug(((Richiamo)i.next()).toString());
        }
    }

      
    /**     
     * metodo che verifica se la pass dell'utente passato come parametro ha valore null
     * @param fname nome dell'utente
     * @param lname cognome dell'utente
     * @return true se la password è null, false altrimenti
     */
    public static boolean isPasswordNull(String fname, String lname){
        
            ResultSet res=(ResultSet)executeQuery("Select password from Pazienti where nome='"
                        +fname+"' and cognome='"+lname+"'", getConnection());
                
                String pwd = null;
            
                try {
                
                    while(res.next()){
                    
                        pwd=res.getString("password");                        
                        log.debug("La password dell'utente è "+pwd);                  
                        
                    }
            
                } catch (SQLException ex) {
                
                    ex.printStackTrace();
                    log.error("No pass");
                    return false;
                    
                }
                //se la pass è null posso registrare il nuovo utente
                if(pwd==null){
                
                    log.debug("Ok puoi settare la pass");
                    return true;
                                
                }
                /*altrimenti l'utente ha già una pass, già registrato nel sistema*/
                else {                
                
                    log.warn("ATTENZIONE! Utente già registrato, non è necessario effettuare la registrazione");
                    return false;
                
                }   
        }   
    
        /**
     * Verifica che l'utente sia presente del db dei pazienti
     * @param fname nome dell'utente da cercare
     * @param  lname cognome dell'utente da cercare
     * @return true se il paziente è nel db, false altrimenti
     */
    
        public static boolean checkUserAndDate(String fname, String lname, String date) {
        
            String query="SELECT * from Pazienti where nome='"+fname+"' and cognome='"+lname+"' and data_di_nascita='"+Date.valueOf(date)+"'";
            boolean res=false;
            
            try{
            
                ResultSet results=DBManager.executeQuery(query, DBManager.getConnection());                   
                return results.next();
            
            } catch(Exception e){
         
                log.error("", e);
            
            }
            return res;
        }
    
       /**
        * metodo che aggiorna la password nel db associata all'utente passato come
        * parametro
        * @param fname nome utente
        * @param lname cognome utente
        * @param pass nuova password da settare
        */
    
        public static void updatePassword(String fname, String lname, String pwd)throws Exception {
        
                try{
                    
                DBManager.executeStatement("UPDATE Pazienti SET password='"+pwd
                        +"' WHERE nome='"+fname+"' AND cognome='"+lname+"'", getConnection());        
                
                } catch(Exception e){
                    
                    log.error("", e);
                    
                }
        
        }
        
        /**         
         * metodo che dato un username e password verifica se è associato a un medico
         * a un paziente o se non è nel db
         * @param fname nome dell'utente
         * @param lname cognome dell'utente
         * @param pass password
         * @return 2 se è paziente, 1 se è medico, -1 in caso di errore
         */
        
        public static int getUserType(String fname, String lname, String pass) {
        
            String paziente_query="SELECT * from Pazienti where nome='"+fname+"' and cognome='"+lname+"' and password='"+pass+"'";
            String medico_query="SELECT * from Medici where nome='"+fname+"' and cognome='"+lname+"' and password='"+pass+"'";
            int res=-1;
            
            try{
            
                ResultSet presults=DBManager.executeQuery(paziente_query, DBManager.getConnection());                   
                ResultSet mresults=DBManager.executeQuery(medico_query, DBManager.getConnection());                   
                
                if(presults.next()){
                
                    res=2;
                }
                else if(mresults.next()){
                
                    res=1;
                }
            
            } catch(Exception e){
            
                log.error("", e);
                return res;
            }
            return res;
        }
        
        /**         
         * metodo che verifica se l'utente dato è presente nel DB
         * @param fname nome dell'utente
         * @param lname cognome dell'utente
         * @return 2 se l'utente è nel DB ed è paziente, 1 se è nel DB ed è medico, -1 altrimenti
         */
        
        public static int checkUser(String fname, String lname) {
        
            String paziente_query="SELECT * from Pazienti where nome='"+fname+"' and cognome='"+lname+"'";
            String medico_query="SELECT * from Medici where nome='"+fname+"' and cognome='"+lname+"'";
            int res=-1;
            
            try{
            
                ResultSet presults=DBManager.executeQuery(paziente_query, DBManager.getConnection());                   
                ResultSet mresults=DBManager.executeQuery(medico_query, DBManager.getConnection());                   
                
                if(presults.next()){
                
                    res=2;
                }
                else if(mresults.next()){
                
                    res=1;
                }
            
            } catch(Exception e){
            
                log.error("", e);
                return res;
            }
            return res;
        }
        
        /**
         * metodo che verifica se la password dell'utente inserito è corretta
         * @param fname nome dell'utente
         * @param lname cognome dell'utente
         * @para pass password da verificare
         * @param type tipo di utente (1 medico, 2 paziente)
         * @return true se la password è corretta, false altrimenti
         */
        public static boolean checkPassword(String fname, String lname, String pass, int type){
        
            String medico="SELECT password FROM Medici WHERE nome='"+fname+"' AND cognome='"+lname+"' and password='"+pass+"'";
            String paziente="SELECT password FROM Pazienti WHERE nome='"+fname+"' AND cognome='"+lname+"' and password='"+pass+"'";
            ResultSet result=null;
            String psw=null;
            boolean res=false;
            
            /*medico*/
            if(type==1){
                
                result=(ResultSet)executeQuery(medico, getConnection());
            
            }        
            /*paziente*/
            if(type==2){
            
                result=(ResultSet)executeQuery(paziente, getConnection());
            }
            
            try{
            
                while(result.next()){
                    
                psw=result.getString("password");  
                if(psw.matches(pass)) {res=true;}
                
                }
            
            }catch(Exception e ){
            
                log.error("", e);
                return res;
            }            
            
            return res;
        }
        
        /**
         * Metodo che resituisce l'id associato al medico passato come parametro
         * @param fname nome del medico
         * @param lname cognome del medico
         * @return l'id del medico, -1 in caso di errore
         */
        
        public static String getMedicoID(String fname, String lname){
        
            String medico="SELECT id_medico FROM Medici WHERE nome='"+fname+"' AND cognome='"+lname+"'";
            String res="";
            ResultSet result=(ResultSet)executeQuery(medico, getConnection());
            
            try{
            
                while(result.next()){
                    
                res=result.getString("id_medico");  
                System.out.println("ID:"+res);
                return res;
                
                }           
            
            }catch(Exception e){
            
                log.error("", e);
                return res;
            }
            
            return res;
        }
        
        /**
         * Metodo che resituisce l'id associato al paziente passato come parametro
         * @param fname nome del paziente   
         * @param lname cognome del paziente
         * @return l'id del paziente, -1 in caso di errore
         */
        
        public static String getPazienteID(String fname, String lname){
        
            String paziente="SELECT id_paziente FROM Pazienti WHERE nome='"+fname+"' AND cognome='"+lname+"'";
            String res="";
            ResultSet result=(ResultSet)executeQuery(paziente, getConnection());
            
            try{
            
                while(result.next()){
                    
                res=result.getString("id_paziente");  
                return res;
                
                }           
            
            }catch(Exception e){
            
                log.error("", e);
                return res;
            }
            
            return res;
        }
}
